package com.loopj.android.http;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.net.HttpURLConnection;

import org.apache.http.Header;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.conn.ClientConnectionManager;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpConnectionParams;

import com.iflytek.elpmobile.utils.io.FileUtils;
import com.loopj.android.http.BigBinaryHttpResponseHandler.IBinaryData;

import android.os.Handler;
import android.os.Handler.Callback;
import android.os.Message;
import android.text.TextUtils;

public class RangeDownloadRequest implements IBinaryData, Callback {

	public static final int SUPPORT_RANGE = 200;
	public static final int NOT_SUPPORT_RANGE = 201;
	public static final int CHECK_FINISH = 100;

	private long mTotalSize = 0;
	private long mDownloadSize = 0;
	private long mCurrentFileSize = 0;
	private int mWriteSize = 0;
	private AsyncHttpClient mClient = new AsyncHttpClient();
	private BigBinaryHttpResponseHandler mResponseHandler;
	private FileOutputStream mTargetStream = null;
	private String mFilePath = "";
	private Handler mHandler = null;

	public static final int TIME_OUT = 20 * 1000;

	private IDownloadCallback mListener = null;

	public interface IDownloadCallback {
		
		public void noSDCard();
		
		public void noSpaceInfo();

		public void cancalDownload();

		public boolean isCancel();

		public void onSuccess();

		public void onProcess(int position);

		public void onFailure();
	}

	private String mUrl = "";
	private RequestParams mRequestParams = null;
	private String[] mAllowedContentTypes = null;
	private boolean mIsCancal,mRangeDownload = false, mIsReachable = true;

	public RangeDownloadRequest(String url, RequestParams params,
			String[] allowedContentTypes) {
		mUrl = url;
		mRequestParams = params;
		mAllowedContentTypes = allowedContentTypes;
	}

	public void setPath(String path) {
		mFilePath = path;
		mDownloadSize = 0;
		mWriteSize = 0;
	}

	public void setDownloadCallback(IDownloadCallback callback) {
		mListener = callback;
	}

	public File getCurrentFile() {
		return new File(mFilePath + ".tmp");
	}

	public void setRangeDownload(boolean value) {
		mRangeDownload = value;
	}

	public void setTimeout(int timeout) {
		if (mClient != null)
			mClient.setTimeout(timeout);
	}

	public void startDownLoad() {
		mIsCancal=false;
		mResponseHandler = new BigBinaryHttpResponseHandler(
				mAllowedContentTypes);
		mResponseHandler.setBinaryHandler(this);

		File targetFile = getCurrentFile();
		File parentFile = targetFile.getParentFile();
		if(!existSDCard()){
			if(mListener!=null)mListener.noSDCard();
			return;
		}
		if (parentFile != null && !parentFile.exists()) {
			targetFile.getParentFile().mkdirs();
		}

		rangeDownload();
	}
	
	private boolean existSDCard() {
	
		return android.os.Environment.getExternalStorageState().equals(
				android.os.Environment.MEDIA_MOUNTED);
	}

	private void checkSupportRange() {
		mHandler = new Handler(this);
		startCheck();
	}

	private void startCheck() {

		new Thread(new Runnable() {

			@Override
			public void run() {
				int statusCode = 300;
				HttpGet httpGet = null;
				HttpClient client = null;

				BasicHttpParams parameters = new BasicHttpParams();
				HttpConnectionParams.setConnectionTimeout(parameters, TIME_OUT);
				HttpConnectionParams.setSoTimeout(parameters, TIME_OUT);

				client = new DefaultHttpClient(parameters);
				httpGet = new HttpGet(mUrl);
				httpGet.addHeader("RANGE", "bytes=" + 0 + "-" + 1);

				try {
					HttpResponse httpResponse = client.execute(httpGet);
					statusCode = httpResponse.getStatusLine().getStatusCode();
					if(statusCode>=300){
						mRangeDownload=mIsReachable = false;
						return;
					}
					if (statusCode != HttpURLConnection.HTTP_PARTIAL) {
						mRangeDownload = false;
					} 
				} catch (Exception e) {
					mRangeDownload = false;
				} finally {
					closeBlockConn(null, httpGet, client);
					mHandler.sendEmptyMessage(CHECK_FINISH);
				}
			}
		}).start();
	}

	private void checkFinish() {
		if (mRangeDownload) {
			mHandler.sendEmptyMessage(SUPPORT_RANGE);
		} else if (mIsReachable) {
			mHandler.sendEmptyMessage(NOT_SUPPORT_RANGE);
		} else {
			mListener.onFailure();
		}
	}

	private void closeBlockConn(InputStream inStream,
			HttpRequestBase requestBase, HttpClient client) {
		FileUtils.closeCloseable(inStream);
		if (null != requestBase)
			requestBase.abort();
		if (null != client) {
			ClientConnectionManager connectManager = client
					.getConnectionManager();
			if (null != connectManager)
				connectManager.shutdown();

		}
	}

	private void rangeDownload() {

		if (mRangeDownload) {
			checkSupportRange();
		} else {
			mClient.get(mUrl, mRequestParams, mResponseHandler);
		}

	}

	private void supportRangeDownload() {
		File targetFile = getCurrentFile();
		FileInputStream stream = null;
		try {
			mCurrentFileSize = targetFile.length();
			mDownloadSize = mCurrentFileSize;
			stream = new FileInputStream(targetFile);
			int fileSize = stream.available();
			mClient.addHeader("RANGE", "bytes=" + fileSize + "-");
		} catch (Exception e) {
			mRangeDownload = false;
			e.printStackTrace();
		} finally {
			FileUtils.closeCloseable(stream);
			if (!mRangeDownload && targetFile.exists())
				targetFile.delete();
			mClient.get(mUrl, mRequestParams, mResponseHandler);
		}
	}

	private void notSupportRangeDownload() {
		File targetFile = getCurrentFile();
		if (targetFile.exists())
			targetFile.delete();
		mClient.get(mUrl, mRequestParams, mResponseHandler);
	}

	public void cancelDownLoad() {
		mIsCancal=true;
		if (mListener != null) mListener.cancalDownload();		
	}

	@Override
	public void onByteBegin(long bufSize) {
		mTotalSize = bufSize;
		FileUtils.closeCloseable(mTargetStream);
		if (TextUtils.isEmpty(mFilePath))
			return;

		File targetFile = getCurrentFile();
		try {
			mTargetStream = new FileOutputStream(targetFile, mRangeDownload);
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		}
	}

	@Override
	public void onByteData(int statusCode, Header[] headers, byte[] buffer,
			int bufSize) {
		if (buffer == null)	return;		
		mDownloadSize += bufSize;
		if (mListener != null) {
			mListener
					.onProcess((int) (((long) mDownloadSize) * 100 / (mTotalSize + mCurrentFileSize)));
		}

		try {
			if (mTargetStream != null) {
				mTargetStream.write(buffer, 0, bufSize);
				mWriteSize += bufSize;
			} else {
				mWriteSize++;
				mWriteSize--;
			}
		} catch (Exception e) {
			if (e.getMessage().contains("ENOSPC") && mListener != null) {
				mListener.noSpaceInfo();
			}
			e.printStackTrace();
		}
	}

	@Override
	public void onByteEnd() {
		if (mListener != null) {			
			if ((mDownloadSize > 0)
					&& (mDownloadSize == mTotalSize + mCurrentFileSize && mTotalSize != 0)) {
				if (mTargetStream != null) {
					FileUtils.closeCloseable(mTargetStream);
					File src = new File(mFilePath + ".tmp");
					File dest = new File(mFilePath);
					src.renameTo(dest);
				}
				mListener.onSuccess();
			} else {
				if (mTargetStream != null)
					FileUtils.closeCloseable(mTargetStream);
				mListener.onFailure();
			}
		}
	}

	@Override
	public boolean isCancel() {
		
		return mIsCancal;
	}

	@Override
	public void onFailure() {
		if (mListener != null)
			mListener.onFailure();
	}

	@Override
	public boolean handleMessage(Message msg) {
		switch (msg.what) {
		case CHECK_FINISH:
			checkFinish();
			break;
		case SUPPORT_RANGE:// ֧�ֶϵ���
			supportRangeDownload();
			break;
		case NOT_SUPPORT_RANGE:
			notSupportRangeDownload();
			break;

		default:
			break;
		}
		return false;
	}
}
